GGally::ggpairs(data.frame(X[,c(1,17:21)]), aes(color=y))Détection d’anomalies
Vous trouverez en haut à droite un interrupteur permettant de passer le rendu en mode sombre/jour à activer selon votre préférence.
Ce rapport cherche à évaluer diverses méthodes de détection d’anomalies, sur un jeu de donnée comportent 6916 observations et 21 dimensions. Il comporte 6666 observations normales et 250 outliers.
1 Présentation des données
Comme on peut le voir dans la Figure 1 qui présente les variables continues, aucune de ces variables ne permet de séparer proprement les outliers.
Voir le code
2 Comparatif courbes roc
2.1 Local Outlier Factor
Nous avons en premier lieu testé l’efficacité de l’algorithme Local Outlier Factor pour la détection des anomalies dans notre jeu de donné.
L’algorithme consiste pour chaque observation a calculer sa distance avec ces k plus proche voisins. On compare ensuite la moyenne de ces distances, à la moyenne des distances des k voisins initiaux avec chacun de leur k plus proche voisin. Ceci nous permet de calculer un score pour chaque observation qui représente le ratio entre ces 2 distances.
Voir le code
best_acc <- 0
best_k <- 0
for(k in 2:15){
lof <- lof(X, minPts = k)
pred <- ifelse(lof==Inf,"outlier","normal")
acc <- sum(pred == data$cat) / length(data$cat)
if(acc > best_acc){
best_acc <- acc
best_k <- k
}
}
lof <- lof(X,minPts = best_k)
predicted <- ifelse(lof>= 2,"outlier","normal")
score<- accuracy(predicted,data$cat)
#print(paste0("Accuracy: ", score, ", k: ",best_k))Après sélection de la meilleur taille de voisinage k, le meilleur modèle semble être k=7 qui retourne une accuracy de 0.927.
2.2 Isolation Forest
Nous avons ensuite testé la méthode de l'Isolation Forest. Cette méthode sépare aléatoirement le jeu de donné en 2 sur une dimension aléatoire. Il répète ce processus pour chaque sous-jeu de donnée jusqu’à isoler une observation. Cette observation devient une feuille de l’arbre et tout le reste du jeu de données devient un nœud. Ce processus peut être répété jusqu’à l’isolation de tous les individus, ou jusqu’à atteindre une profondeur spécifiée. Toutes les feuilles de l’arbre binaire qui en ressort sont donc des observations isolées. Les feuilles en haut de l’arbre sont celles qui ont nécessités le moins de séparations pour être isolé, ont donc plus de chance d’être des outliers. Plusieurs arbres de ce type sont construits et les observations qui se retrouvent le plus souvent en haut des arbres sont ceux qui sont désignés comme outliers.
Bien qu’un grand nombre de paramètres existe, comme pour le random forest nous avons ici uniquement testé le nombre d’arbres construit et la distribution du découpage aléatoire.
Voir le code
best_acc <- 0
best_ntrees <- 0
best_coef <- NULL
for(co in c("uniform","normal")){
for(n in seq(10,200,10)){
iso <- isolation.forest(X,ntrees = n, coefs = co)
pred <- predict(iso,X)
# on affiche comme outlier les 5%de valeurs les plus hautes
predicted <- ifelse(pred>quantile(pred, probs = 0.95),"outlier","normal")
score_iso <- accuracy(predicted,y)
if(score_iso > best_acc){
best_acc <- score_iso
best_ntrees <- n
best_coef <- co
}
}
}
iso <- isolation.forest(X,ntrees = best_ntrees)
pred <- predict(iso,X)
predicted <- ifelse(pred>quantile(pred, probs = 0.95),"outlier","normal")
score_iso <- accuracy(predicted,y)Les résultats de ce test montrent que le nombre d’arbres optimal à construire est 20 et la distribution optimale est normal. Qui donne une accuracy de 0.925.
2.3 One-Class Support vector Machine
Le modèle SVM cherche à trouver un hyperplan qui sépare les observations en classes de manière optimale, i.e en maximisant la marge entre les classes et ce dernier, ce tout en minimisant l’erreur de classification. Contrairement au SVM traditionnel qui est une méthode d’apprentissage supervisé, sa variante One-class SVM est une méthode non supervisée permettant la détéction d’anomalies. Il n’apprend qu’à partir d’une seule classe, celle qui est la plus représentative ou normale dans les données, et modélise la région d’acceptation qui contient la majorité des données.
Nous avons utilisé cette méthode One-class SVM en cherchent les meilleurs paramètres.
Voir le code
best_params_auc <- c()
best_auc <- 0
best_acc <- 0
best_params <- c()
for(gamma in seq(0,1,0.1)){
for(kernel in c("polynomial","linear","radial","sigmoid")){
for(nu in c(0.01,0.05,0.1,0.15,0.2)){
svm <- svm(X,type='one-classification',nu=nu, kernel=kernel,gamma=gamma)
pred_svm <- ifelse(predict(svm,X),"normal","outlier")
auc_svm <- auc(roc(y,svm$decision.values[,1]))
if(auc_svm>best_auc){
best_auc <- auc_svm
best_params_auc<- c("gamma" = gamma,"kernel" = kernel, "nu" = nu)
}
if(accuracy(y,pred_svm)>best_acc){
best_acc <- accuracy(y,pred_svm)
best_params <- c("gamma" = gamma,"kernel" = kernel, "nu" = nu)
}
}
}
}
svm <- svm(X,type="one-classification",nu=best_params["nu"],kernel=best_params["kernel"],gamma=best_params["gamma"])
pred_svm <- predict(svm,X)
pred_svm <- ifelse(pred_svm,"normal","outlier")
tab <- caret::confusionMatrix(table(pred_svm,y))
roc_svm <- roc(y,svm$decision.values[,1])Les meilleurs paramètres pour le SVM semblent être kernel = sigmoid, nu = 0.01, gamma = 0.4. Avec ces paramètres, le modèle donne une accuracy de 0.957.
2.4 Gaussienne multivariée
Le modèle Gaussien cherche à ajuster un modèle Gaussien multivarié sur l’ensemble des variables en estiment les paramètres en maximisant la vraisemblance.
Pour permettre un meilleur fonctionnement du modèle, il sera ajusté uniquement sur les données continues.
Voir le code
gaus = mclust::Mclust(X[,-c(2:16)], G=2)
gaus_pred = predict(gaus)
gaus_acc = sum(ifelse(gaus_pred$classification == 1, "normal", "outlier") == data$cat) / length(data$cat)
roc_gaus = roc(data$cat,gaus_pred$z[,1])2.5 Comparaison des 4 méthodes
La Figure 2 compare les courbes ROC des 4 modèles. On peut y voir que le modèle Local Outlier Factor semble être notablement plus performent.
Voir le code
# on créer un objet roc
roc_lof <- roc(y,-lof)
roc_iso <- roc(y, -pred)
# trace les courbes roc
ggroc(list(LOF = roc_lof, IsolationForest = roc_iso, gaussienne = roc_gaus, SVM = roc_svm))+
annotate(geom="text",x = 0.3,y = 0.4,label = paste0("LOF: AUC=",round(auc(roc_lof),2),", ACCURACY=",round(score,2)))+
annotate(geom="text",x = 0.3,y = 0.35,label = paste0("ISO: AUC=",round(auc(roc_iso),2),", ACCURACY=",round(score_iso,2)))+
annotate(geom="text",x = 0.3,y = 0.3,label = paste0("Gauss: AUC=",round(auc(roc_gaus),2),", ACCURACY=",round(gaus_acc,2)))+
annotate(geom="text",x = 0.3,y = 0.25,label = paste0("SVM: AUC=",round(auc(roc_svm),2),", ACCURACY=",round(tab$overall[1],2)))+
ggtitle("Courbe ROC des 4 modèles")3 Avec standardisation des variables et ACP
Une ACP a été effectuée sur les données standardisées pour voir si cela permet de mieux séparer les outliers.
On peut voir dans la Figure 3 que les résultats de l’ACP semble avoir un peu mieux séparé les outliers que dans les données de base présenté dans la Figure 1.
Nous allons donc retenter les 4 méthodes sur les dimensions retourné par l’ACP.
3.1 LOF
LOF sur ACP
best_acc <- 0
best_k <- 0
for(k in 2:15){
lof <- lof(X_pca, minPts = k)
pred <- ifelse(lof==Inf,"outlier","normal")
acc <- sum(pred == data$cat) / length(data$cat)
if(acc > best_acc){
best_acc <- acc
best_k <- k
}
}
lof_pca <- lof(X_pca,minPts = best_k)
predicted <- ifelse(lof_pca>= 2,"outlier","normal")
score_lof_pca<- accuracy(predicted,data$cat)
score_lof_pca[1] 0.9451995
On peut voir dans la Figure 4 que la projection des données avec à l’ACP a permit d’améliorer la performance du Local Outlier Factor.
3.2 Isolation Forest
La Figure 5 montre que la projection des données avec à l’ACP n’a pas permis l’amélioration des performances de l’Isolation Forest.
Isolation Forest sur ACP
best_acc <- 0
best_ntrees <- 0
best_coef <- NULL
for(co in c("uniform","normal")){
for(n in seq(10,200,10)){
iso <- isolation.forest(X_pca,ntrees = n, coefs = co)
pred <- predict(iso,X)
# on affiche comme outlier les 5%de valeurs les plus hautes
predicted <- ifelse(pred>quantile(pred, probs = 0.95),"outlier","normal")
score_iso <- accuracy(predicted,y)
if(score_iso > best_acc){
best_acc <- score_iso
best_ntrees <- n
best_coef <- co
}
}
}
model_forest <- isolation.forest(X_pca,ntrees = best_ntrees, coefs = best_coef)
pred <- predict(model_forest,X_pca)
predicted <- ifelse(pred>quantile(pred, probs = 0.95),"outlier","normal")
score_iso_pca <- accuracy(predicted,y)3.3 One-Class SVM
One-Class SVM sur ACP
best_params_auc <- c()
best_auc <- 0
best_acc <- 0
best_params <- c()
for(gamma in seq(0,1,0.1)){
for(kernel in c("polynomial","linear","radial","sigmoid")){
for(nu in c(0.01,0.05,0.1,0.15,0.2)){
svm_pca <- svm(X_pca,type='one-classification',nu=nu, kernel=kernel,gamma=gamma)
pred_svm <- ifelse(predict(svm_pca,X_pca),"normal","outlier")
if(length(table(pred_svm)) > 1 & accuracy(y,pred_svm)>best_acc){
best_acc <- accuracy(y,pred_svm)
best_params <- c("gamma" = gamma,"kernel" = kernel, "nu" = nu)
}
}
}
}
svm_pca = svm(X_pca,type='one-classification',nu=best_params["nu"], kernel=best_params["kernel"],gamma=best_params["gamma"])
pred_svm_pca <- predict(svm_pca,X_pca)
pred_svm_pca <- ifelse(pred_svm_pca,"normal","outlier")
tab_svm_pca <- caret::confusionMatrix(table(pred_svm_pca,y))Les meilleurs paramètres pour les données de l’ACP semblent être kernel = sigmoid, nu = 0.01, gamma = 0.4. Avec ces paramètres, le modèle donne une accuracy de 0.956. On remarquera que ce sont les mêmes paramètres que le meilleur modèle pour les données originales.
Bien que l’utilisation de l’ACP a permis d’augmenter l’AUC du SVM, il ne semble pas avoir eu d’effet sur son accuracy (Figure 6).
3.4 Gaussienne multivariée
Gaussienne multivariée sur ACP
gaus_pca = mclust::Mclust(X_pca, G=2)
gaus_pred_pca = predict(gaus_pca)
gaus_acc_pca = sum(ifelse(gaus_pred_pca$classification == 1, "normal", "outlier") == data$cat) / length(data$cat)
roc_gaus_pca = roc(data$cat,gaus_pred_pca$z[,1])Comme le montre la Figure 7 l’utilisation de l’ACP semble avoir grandement réduit l’accuracy et l’AUC du modèle Gaussien.
3.5 Comparaison des 4 méthodes avec ACP
On peut voir dans la Figure 8 que l’utilisation l’ACP à amélioré l’accuracy et l’AUC du Local Outlier Factor, mais à grandement réduit les performances de L’Isolation Forest.
Voir le code
# on créer un objet roc
roc_lof_acp <- roc(y,-lof_pca)
roc_iso_acp <- roc(y, -pred)
roc_svm_pca <- roc(y,svm_pca$decision.values[,1])
roc_gaus_pca <- roc(data$cat,gaus_pred_pca$z[,2])
# trace les courbes roc
ggroc(list(LOF=roc_lof_acp,IsolationForest=roc_iso_acp, SVM = roc_svm_pca, Gaussian = roc_gaus_pca))+
annotate(geom="text",x = 0.7,y = 0.9,label = paste0("LOF: AUC=",round(auc(roc_lof_acp),2),", ACCURACY=",round(score_lof_pca,2)))+
annotate(geom="text",x = 0.7,y = 0.8,label = paste0("ISO: AUC=",round(auc(roc_iso_acp),2),", ACCURACY=",round(score_iso_pca,2)))+
annotate(geom="text",x = 0.7,y = 0.8,label = paste0("SVM: AUC=",round(auc(roc_svm_pca),2),", ACCURACY=",round(tab_svm_pca$overall[1],2)))+
annotate(geom="text",x = 0.7,y = 0.8,label = paste0("Gauss: AUC=",round(auc(roc_gaus_pca),2),", ACCURACY=",round(gaus_acc_pca,2)))+
ggtitle("Courbes ROC des modèles sur ACP normalisé")| LOF | ISO | SVM | Gaussien | |
|---|---|---|---|---|
| AUC sans ACP | 0.682 | 0.608 | 0.674 | 0.941 |
| AUC avec ACP | 0.763 | 0.611 | 0.776 | 0.559 |
| Accuracy sans ACP | 0.927 | 0.915 | 0.957 | 0.881 |
| Accuracy avec ACP | 0.945 | 0.922 | 0.956 | 0.660 |
En regardant la Figure 8 et la Table 1, on peut voir que l’utilisation de l’ACP semble avoir globalement augmenté les performances du Local Outlier Factor et légèrement celle de l’Isolation Forest. Pour le SVM, bien que l’utilisation de ACP augmente l’AUC, il diminue très faiblement l’accuracy. Pour finir, on peut voir que l’utilisation de l’ACP diminue fortement la performance du modèle Gaussien.
4 Conclusion
4.1 Comparaison des meilleurs modèles
Du fait du grand déséquilibre entre les classes, la sélection du meilleur modèle s’est faite sur le critère de l’AUC.
4.2 Pistes annexes
En plus de la comparaison d’un certain de nombre de modèles, nous avons également tenté d’appliquer l’utilisation de matrice spectral à ces différents modèles, mais cela n’a pas aboutit à de meilleurs performances. L’application de cette dernière ne parait pas adapté à la détéction d’outliers.
Afin d’obtenir de meilleurs performances sur nos modèles, nous pourrions également ajuster d’autres hyperparamères qui pourrait avoir une importance pour ce jeu de donnée.